home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / os2 / plnk081.zip / pilot-link.0.8.1 / libsock / pi-file.c < prev    next >
C/C++ Source or Header  |  1997-08-07  |  25KB  |  1,057 lines

  1. /* 
  2.  * Pilot File Interface Library
  3.  * Pace Willisson <pace@blitz.com> December 1996
  4.  * Additions by Kenneth Albanowski
  5.  *
  6.  * This is free software, licensed under the GNU Public License V2.
  7.  * See the file COPYING for details.
  8.  *
  9.  * the following is extracted from the combined wisdom of
  10.  * PDB by Kevin L. Flynn
  11.  * install-prc by Brian J. Swetland, D. Jeff Dionne and Kenneth Albanowski
  12.  * makedoc7 by Pat Beirne, <patb@corel.com>
  13.  * and the include files from the pilot SDK
  14.  */
  15.  
  16.  
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <time.h>
  21.  
  22. #include "pi-source.h"
  23. #include "pi-socket.h"
  24. #include "pi-file.h"
  25.  
  26. #define pi_mktag(c1,c2,c3,c4) (((c1)<<24)|((c2)<<16)|((c3)<<8)|(c4))
  27.  
  28. /*
  29.  * header:
  30.  * 32        name
  31.  * 2        flags
  32.  * 2        version
  33.  * 4        creation time
  34.  * 4         modification time
  35.  * 4        backup time
  36.  * 4        modification number
  37.  * 4        app info offset 
  38.  * 4        sort info offset
  39.  * 4        type
  40.  * 4        creator
  41.  * 4        uniq id seed (I think it is just garbage)
  42.  * 4        next record list id (normally 0, or ptr to extended hdr)
  43.  * 2        num records for this header
  44.  * Hypothetically plus 2 more bytes if an extended or perhaps secondary header (not supported)
  45.  *
  46.  * if the low bit of attr is on, then next thing is a list of resource entry
  47.  * descriptors:
  48.  *
  49.  * resource entry header
  50.  * 4        type
  51.  * 2        id
  52.  * 4        offset
  53.  *
  54.  * otherwise, if the low bit of attr is off, the next thing is a list of
  55.  * record entry decriptors:
  56.  *
  57.  * record entry header
  58.  * 4        offset
  59.  * 1        record attributes
  60.  * 3        unique id
  61.  *
  62.  * then two bytes of unknown purpose, \0\0 seems safe
  63.  *
  64.  * next, the app_info, if any, then the sort_info, if any
  65.  *
  66.  * then the space used the data. Every offset is an offset from the beginning of the
  67.  * file, and will point until this area. Each block starts at the given offset and
  68.  * ends at the beginning of the next block. The last block ends at the end of the file.
  69.  */
  70.  
  71. #define PI_HDR_SIZE 78
  72. #define PI_RESOURCE_ENT_SIZE 10
  73. #define PI_RECORD_ENT_SIZE 8
  74.  
  75. struct pi_file_entry {
  76.   int offset;
  77.   int size;
  78.   unsigned long type;
  79.   int id;
  80.   int attrs;
  81.   pi_uid_t uid;
  82. };
  83.  
  84. struct pi_file {
  85.   int err;
  86.   int for_writing;
  87.   FILE *f;
  88.   FILE *tmpf;
  89.   char *file_name;
  90.  
  91.   struct DBInfo info;
  92.   int app_info_size;
  93.   void *app_info;
  94.   int sort_info_size;
  95.   void *sort_info;
  96.   int next_record_list_id;
  97.   int resource_flag;
  98.   int ent_hdr_size;
  99.  
  100.   unsigned long unique_id_seed;
  101.   int nentries;
  102.   int nentries_allocated;
  103.   struct pi_file_entry *entries;
  104.  
  105.   void *rbuf;
  106.   int rbuf_size;
  107. };
  108.  
  109. static int pi_file_close_for_write (struct pi_file *pf);
  110. static void pi_file_free (struct pi_file *pf);
  111.  
  112. /* this seems to work, but what about leap years? */
  113. /*#define PILOT_TIME_DELTA (((unsigned)(1970 - 1904) * 365 * 24 * 60 * 60) + 1450800)*/
  114.  
  115. /* Exact value of "Jan 1, 1970 0:00:00 GMT" - "Jan 1, 1904 0:00:00 GMT" */
  116. #define PILOT_TIME_DELTA (unsigned)(2082844800)
  117.  
  118.  
  119. /* FIXME: These conversion functions apply no timezone correction. UNIX uses
  120.    UTC for time_t's, while the Pilot uses local time for database backup
  121.    time and appointments, etc. It is not particularly simple to convert
  122.    between these in UNIX, especially since the Pilot's local time is
  123.    unknown, and if syncing over political boundries, could easily be
  124.    different then the local time on the UNIX box. Since the Pilot does not
  125.    know what timezone it is in, there is no unambiguous way to correct for
  126.    this.
  127.    
  128.    Worse, the creation date for a program is stored in the local time _of
  129.    the computer which did the final linking of that program_. Again, the
  130.    Pilot does not store the timezone information needed to reconstruct
  131.    where/when this was.
  132.    
  133.    A better immediate tack would be to dissect these into struct tm's, and
  134.    return those.
  135.                                                                      --KJA
  136.    */
  137.    
  138. static time_t
  139. pilot_time_to_unix_time (unsigned long raw_time)
  140. {
  141.   return (time_t)(raw_time - PILOT_TIME_DELTA);
  142. }
  143.  
  144. static unsigned long
  145. unix_time_to_pilot_time (time_t t)
  146. {
  147.   return (unsigned long)((unsigned long)t + PILOT_TIME_DELTA);
  148. }
  149.  
  150. /*
  151.  * open .prc or .pdb file for reading
  152.  */
  153. struct pi_file *
  154. pi_file_open (char *name)
  155. {
  156.   struct pi_file *pf;
  157.   struct DBInfo *ip;
  158.   unsigned char buf[PI_HDR_SIZE];
  159.   unsigned char *p;
  160.   int file_size;
  161.   unsigned long offset, app_info_offset, sort_info_offset;
  162.   int total_hdr_size;
  163.   int i;
  164.   struct pi_file_entry *entp;
  165.  
  166.   if ((pf = calloc (1, sizeof *pf)) == NULL)
  167.     return (NULL);
  168.  
  169.   pf->for_writing = 0;
  170.  
  171.   if ((pf->f = fopen (name, "rb")) == NULL)
  172.     goto bad;
  173.  
  174.   fseek (pf->f, 0, SEEK_END);
  175.   file_size = ftell (pf->f);
  176.   fseek (pf->f, 0, SEEK_SET);
  177.  
  178.   if (fread (buf, PI_HDR_SIZE, 1, pf->f) != (size_t)1) {
  179.     fprintf (stderr, "%s: can't read header\n", name);
  180.     goto bad;
  181.   }
  182.  
  183.   p = buf;
  184.   ip = &pf->info;
  185.  
  186.   memcpy(ip->name, p, 32);
  187.   ip->flags = get_short(p+32);
  188.   ip->version = get_short(p+34);
  189.   ip->createDate = pilot_time_to_unix_time (get_long(p+36));
  190.   ip->modifyDate = pilot_time_to_unix_time (get_long(p+40));
  191.   ip->backupDate = pilot_time_to_unix_time (get_long(p+44));
  192.   ip->modnum = get_long(p+48);
  193.   app_info_offset = get_long(p+52);
  194.   sort_info_offset = get_long(p+56);
  195.   ip->type = get_long(p+60);
  196.   ip->creator = get_long(p+64);
  197.   pf->unique_id_seed = get_long(p+68);
  198.   
  199.   /* record list header */
  200.   pf->next_record_list_id = get_long(p+72);
  201.   pf->nentries = get_short(p+76);
  202.  
  203. #ifdef DEBUG  
  204.   printf("pi_file_open:\n Name '%s', flags 0x%4.4X, version %d\n", ip->name, ip->flags, ip->version);
  205.   printf(" Creation date %s\n", ctime(&ip->createDate));
  206.   printf(" Modification date %s\n", ctime(&ip->modifyDate));
  207.   printf(" Backup date %s\n", ctime(&ip->backupDate));
  208.   printf(" Appinfo size %d, sortinfo size %d\n", pf->app_info_size, pf->sort_info_size);
  209.   printf(" Type '%s'", printlong(ip->type));
  210.   printf(" Creator '%s', seed 0x%8.8lX\n", printlong(ip->creator), pf->unique_id_seed);
  211. #endif
  212.   
  213.   if (pf->next_record_list_id != 0) {
  214.     fprintf (stderr, "%s: extended format not supported\n", name);
  215.     goto bad;
  216.   }
  217.     
  218.   if (ip->flags & dlpDBFlagResource) {
  219.     pf->resource_flag = 1;
  220.     pf->ent_hdr_size = PI_RESOURCE_ENT_SIZE;
  221.   } else {
  222.     pf->resource_flag = 0;
  223.     pf->ent_hdr_size = PI_RECORD_ENT_SIZE;
  224.   }
  225.  
  226.   if (pf->app_info_size < 0 || pf->sort_info_size < 0 || pf->nentries < 0) {
  227.     fprintf (stderr, "%s: bad header\n", name);
  228.     goto bad;
  229.   }
  230.  
  231.   total_hdr_size = PI_HDR_SIZE + pf->app_info_size + pf->sort_info_size
  232.     + pf->nentries * pf->ent_hdr_size;
  233.  
  234.   if (total_hdr_size > file_size) {
  235.     fprintf (stderr, "%s: file too short\n", name);
  236.     goto bad;
  237.   }
  238.  
  239.   offset = file_size;
  240.  
  241.   if (pf->nentries) {
  242.     if ((pf->entries = calloc (pf->nentries, sizeof *pf->entries)) == NULL)
  243.       goto bad;
  244.     
  245.     for (i = 0, entp = pf->entries; i < pf->nentries; i++, entp++) {
  246.       if (fread (buf, pf->ent_hdr_size, 1, pf->f) != (size_t)1)
  247.       goto bad;
  248.  
  249.       p = buf;
  250.       if (pf->resource_flag) {
  251.         entp->type = get_long(p);
  252.         entp->id = get_short(p+4);
  253.         entp->offset = get_long(p+6);
  254. #ifdef DEBUG
  255.         printf("Entry %d '%s' #%d @%X\n", i, printlong(entp->type), entp->id, entp->offset);
  256. #endif
  257.       } else { 
  258.         entp->offset = get_long(p);
  259.         entp->attrs = get_byte(p+4);
  260.         entp->uid = get_treble(p+5);
  261. #ifdef DEBUG
  262.         printf("Entry %d 0x%8.8X %2.2X @%X\n", i, (int)entp->uid, entp->attrs, entp->offset);
  263. #endif
  264.       }
  265.     }
  266.     
  267.     for (i = 0, entp = pf->entries+pf->nentries-1; i < pf->nentries; i++, entp--) {
  268.       entp->size = offset - entp->offset;
  269.       offset = entp->offset;
  270. #ifdef DEBUG
  271.       printf("Entry %d, size %d\n",pf->nentries-i-1, entp->size);
  272. #endif
  273.     }
  274.   }
  275.   
  276.   if (sort_info_offset) {
  277.     pf->sort_info_size = offset - sort_info_offset;
  278.     offset = sort_info_offset;
  279. #ifdef DEBUG
  280.     printf("Sort info, size %d\n",pf->sort_info_size);
  281. #endif
  282.   }
  283.  
  284.   if (app_info_offset) {
  285.     pf->app_info_size = offset - app_info_offset;
  286.     offset = app_info_offset;
  287. #ifdef DEBUG
  288.     printf("App info, size %d\n",pf->app_info_size);
  289. #endif
  290.   }
  291.  
  292.   if (pf->app_info_size == 0)
  293.     pf->app_info = NULL;
  294.   else {
  295.     if ((pf->app_info = malloc (pf->app_info_size)) == NULL)
  296.       goto bad;
  297.     fseek(pf->f, app_info_offset, SEEK_SET);
  298.     if (fread (pf->app_info, 1, pf->app_info_size, pf->f) != (size_t)pf->app_info_size)
  299.       goto bad;
  300.   }
  301.  
  302.   if (pf->sort_info_size == 0)
  303.     pf->sort_info = NULL;
  304.   else {
  305.     if ((pf->sort_info = malloc (pf->sort_info_size)) == NULL)
  306.       goto bad;
  307.     fseek(pf->f, sort_info_offset, SEEK_SET);
  308.     if (fread(pf->sort_info, 1, pf->sort_info_size, pf->f) != (size_t)pf->sort_info_size)
  309.       goto bad;
  310.   }
  311.  
  312.   return (pf);
  313.  
  314. bad:
  315.   pi_file_close (pf);
  316.   return (NULL);
  317. }
  318.  
  319. int
  320. pi_file_close (struct pi_file *pf)
  321. {
  322.   int err;
  323.   if (pf->for_writing) {
  324.     if (pi_file_close_for_write (pf) < 0)
  325.       pf->err = 1;
  326.   }
  327.  
  328.   err = pf->err;
  329.  
  330.   pi_file_free (pf);
  331.  
  332.   if (err)
  333.     return (-1);
  334.  
  335.   return (0);
  336. }
  337.  
  338. static void
  339. pi_file_free (struct pi_file *pf)
  340. {
  341.   if (pf->f)
  342.     fclose (pf->f);
  343.   if (pf->app_info)
  344.     free (pf->app_info);
  345.   if (pf->sort_info)
  346.     free (pf->sort_info);
  347.   if (pf->entries)
  348.     free (pf->entries);
  349.   if (pf->file_name)
  350.     free (pf->file_name);
  351.   if (pf->rbuf)
  352.     free(pf->rbuf);
  353.   if (pf->tmpf)
  354.     fclose (pf->tmpf);
  355.   free (pf);
  356. }
  357.  
  358. int
  359. pi_file_get_info (struct pi_file *pf, struct DBInfo *infop)
  360. {
  361.   *infop = pf->info;
  362.   return (0);
  363. }
  364.  
  365. int
  366. pi_file_get_app_info (struct pi_file *pf, void **datap, int *sizep)
  367. {
  368.   *datap = pf->app_info;
  369.   *sizep = pf->app_info_size;
  370.   return (0);
  371. }
  372.  
  373. int
  374. pi_file_get_sort_info (struct pi_file *pf, void **datap, int *sizep)
  375. {
  376.   *datap = pf->sort_info;
  377.   *sizep = pf->sort_info_size;
  378.   return (0);
  379. }
  380.  
  381. static int
  382. pi_file_set_rbuf_size (struct pi_file *pf, int size)
  383. {
  384.   int new_size;
  385.   void *rbuf;
  386.  
  387.   if (size > pf->rbuf_size) {
  388.     if (pf->rbuf_size == 0) {
  389.       new_size = size+2048;
  390.       rbuf = malloc (new_size);
  391.     } else {
  392.       new_size = size+2048;
  393.       rbuf = realloc (pf->rbuf, new_size);
  394.     }
  395.  
  396.     if (rbuf == NULL)
  397.       return (-1);
  398.     
  399.     pf->rbuf_size = new_size;
  400.     pf->rbuf = rbuf;
  401.   }
  402.  
  403.   return (0);
  404. }
  405.  
  406. /* returned buffer is valid until next call, or until pi_file_close */
  407. int
  408. pi_file_read_resource (struct pi_file *pf, int idx,
  409.                void **bufp, int *sizep, unsigned long *type, int *idp)
  410. {
  411.   struct DBInfo *ip;
  412.   struct pi_file_entry *entp;
  413.  
  414.   if (pf->for_writing)
  415.     return (-1);
  416.  
  417.   ip = &pf->info;
  418.  
  419.   if (!pf->resource_flag)
  420.     return (-1);
  421.  
  422.   if (idx < 0 || idx >= pf->nentries)
  423.     return (-1);
  424.  
  425.   entp = &pf->entries[idx];
  426.  
  427.   if (bufp) {
  428.     if (pi_file_set_rbuf_size (pf, entp->size) < 0)
  429.       return (-1);
  430.     fseek (pf->f, pf->entries[idx].offset, SEEK_SET);
  431.     if (fread (pf->rbuf, 1, entp->size, pf->f) != (size_t)entp->size)
  432.       return (-1);
  433.     *bufp = pf->rbuf;
  434.   }
  435.   
  436.   if (sizep)
  437.     *sizep = entp->size;
  438.   if (type)
  439.     *type = entp->type;
  440.   if (idp)
  441.     *idp = entp->id;
  442.   
  443.   return (0);
  444. }
  445.  
  446. /* returned buffer is valid until next call, or until pi_file_close */
  447. int
  448. pi_file_read_record (struct pi_file *pf, int idx,
  449.              void **bufp, int *sizep, int *attrp, int *catp, pi_uid_t *uidp)
  450. {
  451.   struct DBInfo *ip;
  452.   struct pi_file_entry *entp;
  453.  
  454.   if (pf->for_writing)
  455.     return (-1);
  456.     
  457. #ifdef DEBUG
  458.   fprintf(stderr, "Reading record %d\n", idx);
  459. #endif  
  460.  
  461.   ip = &pf->info;
  462.  
  463.   if (pf->resource_flag)
  464.     return (-1);
  465.  
  466.   if (idx < 0 || idx >= pf->nentries)
  467.     return (-1);
  468.  
  469.   entp = &pf->entries[idx];
  470.   
  471. #ifdef DEBUG
  472.   fprintf(stderr, "record size is %d\n", entp->size);
  473. #endif  
  474.  
  475.  
  476.   if (bufp) {
  477.     if (pi_file_set_rbuf_size (pf, entp->size) < 0)
  478.       return (-1);
  479.     fseek (pf->f, pf->entries[idx].offset, SEEK_SET);
  480.     if (fread (pf->rbuf, 1, entp->size, pf->f) != (size_t)entp->size)
  481.       return (-1);
  482.     *bufp = pf->rbuf;
  483.   }
  484.   
  485.   if (sizep)
  486.     *sizep = entp->size;
  487.   if (attrp)
  488.     *attrp = entp->attrs & 0xf0;
  489.   if (catp)
  490.     *catp = entp->attrs & 0xf;
  491.   if (uidp)
  492.     *uidp = entp->uid;
  493.  
  494.   return (0);
  495. }
  496.  
  497. int
  498. pi_file_read_record_by_id (struct pi_file *pf, pi_uid_t uid,
  499.                void **bufp, int *sizep, int *idxp, int *attrp, int *catp)
  500. {
  501.   int idx;
  502.   struct pi_file_entry *entp;
  503.  
  504.   for (idx = 0, entp = pf->entries; idx < pf->nentries; idx++, entp++) {
  505.     if (entp->uid == uid) {
  506.       if (idxp)
  507.         *idxp = idx;
  508.       return (pi_file_read_record (pf, idx, bufp, sizep, attrp, catp, &uid));
  509.     }
  510.   }
  511.  
  512.   return (-1);
  513. }
  514.  
  515. int
  516. pi_file_id_used (struct pi_file *pf, pi_uid_t uid)
  517. {
  518.   int idx;
  519.   struct pi_file_entry *entp;
  520.  
  521.   for (idx = 0, entp = pf->entries; idx < pf->nentries; idx++, entp++) {
  522.     if (entp->uid == uid)
  523.       return (1);
  524.   }
  525.   return (0);
  526. }
  527.  
  528. struct pi_file *
  529. pi_file_create (char *name, struct DBInfo * info)
  530. {
  531.   struct pi_file *pf;
  532.  
  533.   if ((pf = calloc (1, sizeof *pf)) == NULL)
  534.     return (NULL);
  535.  
  536.   if ((pf->file_name = strdup (name)) == NULL)
  537.     goto bad;
  538.  
  539.   pf->for_writing = 1;
  540.   pf->info = *info;
  541.  
  542.   if (info->flags & dlpDBFlagResource) {
  543.     pf->resource_flag = 1;
  544.     pf->ent_hdr_size = PI_RESOURCE_ENT_SIZE;
  545.   } else {
  546.     pf->resource_flag = 0;
  547.     pf->ent_hdr_size = PI_RECORD_ENT_SIZE;
  548.   }
  549.  
  550.   if ((pf->tmpf = tmpfile ()) == NULL)
  551.     goto bad;
  552.  
  553.   return (pf);
  554.  
  555. bad:
  556.   pi_file_free (pf);
  557.   return (NULL);
  558. }
  559.  
  560. /* may call these any time before close (even multiple times) */
  561. int
  562. pi_file_set_info (struct pi_file *pf, struct DBInfo *ip)
  563. {
  564.   if (!pf->for_writing)
  565.     return (-1);
  566.  
  567.   if ((ip->flags & dlpDBFlagResource) != (pf->info.flags & dlpDBFlagResource))
  568.       return (-1);
  569.  
  570.   pf->info = *ip;
  571.  
  572.   return (0);
  573. }
  574.  
  575. int
  576. pi_file_set_app_info (struct pi_file *pf, void *data, int size)
  577. {
  578.   void *p;
  579.  
  580.   if (!size) {
  581.     if (pf->app_info)
  582.       free (pf->app_info);
  583.     pf->app_info_size = 0;
  584.     return (0);
  585.   }
  586.  
  587.   if ((p = malloc (size)) == NULL)
  588.     return (-1);
  589.   memcpy (p, data, size);
  590.  
  591.   if (pf->app_info)
  592.     free (pf->app_info);
  593.   pf->app_info = p;
  594.   pf->app_info_size = size;
  595.   return (0);
  596. }
  597.   
  598. int
  599. pi_file_set_sort_info (struct pi_file *pf, void *data, int size)
  600. {
  601.   void *p;
  602.  
  603.   if (!size) {
  604.     if (pf->sort_info)
  605.       free (pf->sort_info);
  606.     pf->sort_info_size = 0;
  607.     return (0);
  608.   }
  609.  
  610.   if ((p = malloc (size)) == NULL)
  611.     return (-1);
  612.   memcpy (p, data, size);
  613.  
  614.   if (pf->sort_info)
  615.     free (pf->sort_info);
  616.   pf->sort_info = p;
  617.   pf->sort_info_size = size;
  618.   return (0);
  619. }
  620.   
  621. /*
  622.  * internal function to extend entry list if necessary, and return a
  623.  * pointer to the next available slot
  624.  */
  625. static struct pi_file_entry *
  626. pi_file_append_entry (struct pi_file *pf)
  627. {
  628.   struct DBInfo *ip;
  629.   int new_count;
  630.   int new_size;
  631.   struct pi_file_entry *new_entries;
  632.   struct pi_file_entry *entp;
  633.  
  634.   ip = &pf->info;
  635.  
  636.   if (pf->nentries >= pf->nentries_allocated) {
  637.     if (pf->nentries_allocated == 0)
  638.       new_count = 100;
  639.     else
  640.       new_count = pf->nentries_allocated * 3 / 2;
  641.     new_size = new_count * sizeof *pf->entries;
  642.  
  643.     if (pf->entries == NULL)
  644.       new_entries = malloc (new_size);
  645.     else
  646.       new_entries = realloc (pf->entries, new_size);
  647.  
  648.     if (new_entries == NULL)
  649.       return (NULL);
  650.   
  651.     pf->nentries_allocated = new_count;
  652.     pf->entries = new_entries;
  653.   }
  654.  
  655.   entp = &pf->entries[pf->nentries++];
  656.   memset (entp, 0, sizeof *entp);
  657.   return (entp);
  658. }
  659.  
  660. int
  661. pi_file_append_resource (struct pi_file *pf, void *buf, int size,
  662.              unsigned long type, int id)
  663. {
  664.   struct pi_file_entry *entp;
  665.  
  666.   if (!pf->for_writing || !pf->resource_flag)
  667.     return (-1);
  668.  
  669.   entp = pi_file_append_entry (pf);
  670.   
  671.   if (size && (fwrite (buf, size, 1, pf->tmpf) != 1)) {
  672.     pf->err = 1;
  673.     return (-1);
  674.   }
  675.  
  676.   entp->size = size;
  677.   entp->type = type;
  678.   entp->id = id;
  679.  
  680.   return (0);
  681. }
  682.  
  683. int pi_file_append_record (struct pi_file *pf, void *buf, int size,
  684.                int attrs, int category, pi_uid_t uid)
  685. {
  686.   struct pi_file_entry *entp;
  687.  
  688. #ifdef DEBUG
  689.   printf ("append: %d\n", pf->nentries);
  690. #endif
  691.  
  692.   if (!pf->for_writing || pf->resource_flag)
  693.     return (-1);
  694.  
  695.   entp = pi_file_append_entry (pf);
  696.   
  697.   if (size && (fwrite (buf, size, 1, pf->tmpf) != 1)) {
  698.     pf->err = 1;
  699.     return (-1);
  700.   }
  701.  
  702.   entp->size = size;
  703.   entp->attrs = (attrs & 0xf0) | (category & 0xf);
  704.   entp->uid = uid;
  705.  
  706.   return (0);
  707. }
  708.  
  709. int pi_file_get_entries (struct pi_file * pf, int * entries)
  710. {
  711.   *entries = pf->nentries;
  712.   
  713.   return (0);
  714. }
  715.  
  716. static int
  717. pi_file_close_for_write (struct pi_file *pf)
  718. {
  719.   FILE *f;
  720.   struct DBInfo *ip;
  721.   unsigned char buf[PI_HDR_SIZE];
  722.   unsigned char *p;
  723.   int offset;
  724.   int i;
  725.   struct pi_file_entry *entp;
  726.   int c;
  727.  
  728.   ip = &pf->info;
  729.   if (pf->nentries >= 64*1024) {
  730.     printf ("too many entries for this implentation of pi-file: %d\n",
  731.         pf->nentries);
  732.     return (-1);
  733.   }
  734.  
  735.   if ((f = fopen (pf->file_name, "wb")) == NULL)
  736.     return (-1);
  737.  
  738.   ip = &pf->info;
  739.  
  740.   offset = PI_HDR_SIZE + pf->nentries * pf->ent_hdr_size + 2;
  741.  
  742.   p = buf;
  743.   memcpy(p, ip->name, 32);
  744.   set_short(p+32, ip->flags);
  745.   set_short(p+34, ip->version);
  746.   set_long(p+36, unix_time_to_pilot_time(ip->createDate));
  747.   set_long(p+40, unix_time_to_pilot_time(ip->modifyDate));
  748.   set_long(p+44, unix_time_to_pilot_time(ip->backupDate));
  749.   set_long(p+48, ip->modnum);
  750.   set_long(p+52, pf->app_info_size ? offset : 0); offset += pf->app_info_size;
  751.   set_long(p+56, pf->sort_info_size ? offset : 0); offset += pf->sort_info_size;
  752.   set_long(p+60, ip->type);
  753.   set_long(p+64, ip->creator);
  754.   set_long(p+68, pf->unique_id_seed);
  755.   set_long(p+72, pf->next_record_list_id);
  756.   set_short(p+76, pf->nentries);
  757.  
  758.   if (fwrite (buf, PI_HDR_SIZE, 1, f) != 1)
  759.     goto bad;
  760.  
  761.   for (i = 0, entp = pf->entries; i < pf->nentries; i++, entp++) {
  762.     entp->offset = offset;
  763.     
  764.     p = buf;
  765.     if (pf->resource_flag) {
  766.       set_long(p, entp->type);
  767.       set_short(p+4, entp->id);
  768.       set_long(p+6, entp->offset);
  769.     } else {
  770.       set_long(p, entp->offset);
  771.       set_byte(p+4, entp->attrs);
  772.       set_treble(p+5, entp->uid);
  773.     }
  774.  
  775.     if (fwrite (buf, pf->ent_hdr_size, 1, f) != 1)
  776.       goto bad;
  777.  
  778.     offset += entp->size;
  779.   }
  780.   
  781.   /* This may just be packing */
  782.   fwrite( "\0\0", 1, 2, f);
  783.   
  784.   if (pf->app_info && (fwrite (pf->app_info, 1, pf->app_info_size, f) != (size_t)pf->app_info_size))
  785.     goto bad;
  786.  
  787.   if (pf->sort_info && (fwrite (pf->sort_info, 1, pf->sort_info_size, f) != (size_t)pf->sort_info_size))
  788.     goto bad;
  789.     
  790.  
  791.   rewind (pf->tmpf);
  792.   while ((c = getc (pf->tmpf)) != EOF)
  793.     putc (c, f);
  794.  
  795.   fflush (f);
  796.  
  797.   if (ferror (f) || feof (f))
  798.     goto bad;
  799.  
  800.   fclose (f);
  801.   return (0);
  802.  
  803. bad:
  804.   fclose (f);
  805.   return (-1);
  806. }
  807.  
  808. int pi_file_retrieve(struct pi_file * pf, int socket, int cardno)
  809. {
  810.   int db;
  811.   int l,j;
  812.   unsigned char buffer[0xffff];
  813.   if(dlp_OpenDB(socket, cardno, dlpOpenRead|dlpOpenSecret, pf->info.name, &db)<0)
  814.     return -1;
  815.     
  816.   l = dlp_ReadAppBlock(socket, db, 0, buffer, 0xffff);
  817.   if(l>0)
  818.     pi_file_set_app_info(pf, buffer, l);
  819.  
  820.   if(dlp_ReadOpenDBInfo(socket, db, &l)<0)
  821.     return -1;
  822.       
  823.   if(pf->info.flags & dlpDBFlagResource)
  824.     for(j=0;j<l;j++) {
  825.       unsigned long type;
  826.       int id;
  827.       int size;
  828.       if( (dlp_ReadResourceByIndex(socket, db, j, buffer, &type, &id, &size)<0) ||
  829.         (pi_file_append_resource(pf, buffer, size, type, id)<0)) {
  830.         dlp_CloseDB(socket, db);
  831.         return -1;
  832.       }
  833.     }
  834.   else
  835.     for(j=0;j<l;j++) {
  836.       unsigned long id;
  837.         int size;
  838.         int attr;
  839.         int category;
  840.         if( (dlp_ReadRecordByIndex(socket, db, j, buffer, &id, &size, &attr, &category)<0)) {
  841.           dlp_CloseDB(socket,db);
  842.           return -1;
  843.         }
  844.         /* There is no way to restore records with these attributes, so there is no
  845.            use in backing them up */
  846.         if (attr & (dlpRecAttrArchived|dlpRecAttrDeleted))
  847.           continue;
  848.         if (pi_file_append_record(pf, buffer, size, attr, category, id)<0) {
  849.             dlp_CloseDB(socket, db);
  850.             return -1;
  851.           }
  852.       }
  853.       
  854.   return dlp_CloseDB(socket, db);
  855. }      
  856.  
  857. int pi_file_install(struct pi_file * pf, int socket, int cardno)
  858. {
  859.   int db;
  860.   int l,j;
  861.   int reset = 0;
  862.   int flags;
  863.   int version;
  864.   void * buffer;
  865.   int freeai = 0;
  866.   
  867.   version = pi_version(socket);
  868.   
  869.   /* Delete DB if it already exists */
  870.   dlp_DeleteDB(socket, cardno, pf->info.name);
  871.   
  872.   /* Set up DB flags */
  873.   flags = pf->info.flags;
  874.   
  875.   if (strcmp(pf->info.name, "Graffiti ShortCuts ")==0) {
  876.     flags |= 0x8000; /* Rewrite an open DB */
  877.     reset = 1; /* To be on the safe side */
  878.   }
  879. #ifdef DEBUG
  880.   printf("Flags = %8.8X, name = '%s'\n", flags, pf->info.name);
  881. #endif
  882.   
  883.   /* Create DB*/
  884.   if(dlp_CreateDB(socket, pf->info.creator, pf->info.type, cardno,
  885.                        flags, pf->info.version,
  886.                        pf->info.name, &db)<0)
  887.     return -1;
  888.     
  889.   
  890.   pi_file_get_app_info(pf, &buffer, &l);
  891.   
  892.   /* Compensate for bug in OS 2.x Memo */
  893.   if((version > 0x0100) && (strcmp(pf->info.name, "MemoDB")==0) && (l>0) && (l<282))
  894.   {
  895.       /* Justification: The appInfo structure was accidentally lengthend in
  896.          OS 2.0, but the Memo application does not check that it is long
  897.          enough, hence the shorter block from OS 1.x will cause the 2.0 Memo
  898.          application to lock up if the sort preferences are modified. This
  899.          code detects the installation of a short app info block on a 2.0
  900.          machine, and lengthens it. This transformation will never lose
  901.          information. */
  902.  
  903.       void * b2 = calloc(1, 282);
  904.       memcpy(b2, buffer, l);
  905.       buffer = b2;
  906.       l = 282;
  907.       freeai = 1;
  908.   }
  909.   
  910.   /* All system updates seen to have the 'ptch' type, so trigger a reboot on those */
  911.   if (pf->info.creator == pi_mktag('p','t','c','h'))
  912.     reset = 1; 
  913.    
  914.   if (pf->info.flags & dlpDBFlagReset)
  915.     reset = 1;
  916.       
  917.   if(l>0)
  918.     dlp_WriteAppBlock(socket, db, buffer, l);
  919.   
  920.   if (freeai)
  921.     free(buffer);
  922.    
  923.   /* Resource or record? */
  924.   if(pf->info.flags & dlpDBFlagResource) {
  925.     for(j=0;j<pf->nentries;j++) {
  926.       int size;
  927.       if( (pi_file_read_resource(pf, j, 0, &size, 0, 0)==0) &&
  928.           (size > 65536)) {
  929.     fprintf(stderr, "Database contains resource over 64K!\n");
  930.         goto fail;
  931.       }
  932.     }
  933.     for(j=0;j<pf->nentries;j++) {
  934.       unsigned long type;
  935.       int id;
  936.       int size;
  937.       if( (pi_file_read_resource(pf, j, &buffer, &size, &type, &id)<0) ||
  938.           (dlp_WriteResource(socket, db, type, id, buffer, size)<0) ) {
  939.         goto fail;
  940.       }
  941.  
  942.       /* If we see a 'boot' section, regardless of file type, require reset */
  943.       if (type == pi_mktag ('b','o','o','t')) 
  944.         reset = 1;
  945.     }
  946.   } else {
  947.     for(j=0;j<pf->nentries;j++) {
  948.       int size;
  949.       if( ((pi_file_read_record(pf, j, 0, &size, 0, 0, 0)==0)) &&
  950.           (size > 65536)) {
  951.     fprintf(stderr, "Database contains record over 64K!\n");
  952.         goto fail;
  953.       }
  954.     }
  955.     for(j=0;j<pf->nentries;j++) {
  956.       unsigned long id;
  957.       int size;
  958.       int attr;
  959.       int category;
  960.       if(pi_file_read_record(pf, j, &buffer, &size, &attr, &category, &id)<0)
  961.         goto fail;
  962.       if ((attr & (dlpRecAttrArchived|dlpRecAttrDeleted)) &&
  963.          (version < 0x0101))
  964.          continue; /* Old OS cannot install deleted records, so don't try */
  965.       if(dlp_WriteRecord(socket, db, attr, id, category, buffer, size, 0)<0)
  966.         goto fail;
  967.     }
  968.   }
  969.   
  970.   if(reset)
  971.     dlp_ResetSystem(socket);
  972.       
  973.   return dlp_CloseDB(socket, db);
  974.  
  975. fail:
  976.   dlp_CloseDB(socket, db);
  977.   dlp_DeleteDB(socket, cardno, pf->info.name);
  978.   return -1;
  979. }      
  980.  
  981. int pi_file_merge(struct pi_file * pf, int socket, int cardno)
  982. {
  983.   int db;
  984.   int j;
  985.   int reset = 0;
  986.   int version;
  987.   void * buffer;
  988.   
  989.   version = pi_version(socket);
  990.   
  991.   if(dlp_OpenDB(socket, cardno, dlpOpenReadWrite|dlpOpenSecret, pf->info.name, &db)<0)
  992.     return pi_file_install(pf, socket, cardno);
  993.   
  994.   /* All system updates seen to have the 'ptch' type, so trigger a reboot on those */
  995.   if (pf->info.creator == pi_mktag('p','t','c','h'))
  996.     reset = 1; 
  997.    
  998.   if (pf->info.flags & dlpDBFlagReset)
  999.     reset = 1;
  1000.       
  1001.   /* Resource or record? */
  1002.   if(pf->info.flags & dlpDBFlagResource) {
  1003.     for(j=0;j<pf->nentries;j++) {
  1004.       int size;
  1005.       if( (pi_file_read_resource(pf, j, 0, &size, 0, 0)==0) &&
  1006.           (size > 65536)) {
  1007.     fprintf(stderr, "Database contains resource over 64K!\n");
  1008.         goto fail;
  1009.       }
  1010.     }
  1011.     for(j=0;j<pf->nentries;j++) {
  1012.       unsigned long type;
  1013.       int id;
  1014.       int size;
  1015.       if( (pi_file_read_resource(pf, j, &buffer, &size, &type, &id)<0) ||
  1016.           (dlp_WriteResource(socket, db, type, id, buffer, size)<0) ) {
  1017.         goto fail;
  1018.       }
  1019.  
  1020.       /* If we see a 'boot' section, regardless of file type, require reset */
  1021.       if (type == pi_mktag ('b','o','o','t')) 
  1022.         reset = 1;
  1023.     }
  1024.   } else {
  1025.     for(j=0;j<pf->nentries;j++) {
  1026.       int size;
  1027.       if( ((pi_file_read_record(pf, j, 0, &size, 0, 0, 0)==0)) &&
  1028.           (size > 65536)) {
  1029.     fprintf(stderr, "Database contains record over 64K!\n");
  1030.         goto fail;
  1031.       }
  1032.     }
  1033.     for(j=0;j<pf->nentries;j++) {
  1034.       unsigned long id;
  1035.       int size;
  1036.       int attr;
  1037.       int category;
  1038.       if(pi_file_read_record(pf, j, &buffer, &size, &attr, &category, &id)<0)
  1039.         goto fail;
  1040.       if ((attr & (dlpRecAttrArchived|dlpRecAttrDeleted)) &&
  1041.          (version < 0x0101))
  1042.          continue; /* Old OS cannot install deleted records, so don't try */
  1043.       if(dlp_WriteRecord(socket, db, attr, 0, category, buffer, size, 0)<0)
  1044.         goto fail;
  1045.     }
  1046.   }
  1047.   
  1048.   if(reset)
  1049.     dlp_ResetSystem(socket);
  1050.       
  1051.   return dlp_CloseDB(socket, db);
  1052.  
  1053. fail:
  1054.   dlp_CloseDB(socket, db);
  1055.   return -1;
  1056. }      
  1057.